home *** CD-ROM | disk | FTP | other *** search
- #include "stdafx.h"
-
- cPush::cPush(cPush **list, fix duration, fix _vx, fix _vy, fix _ax, fix _ay)
- {
- vx = _vx;
- vy = _vy;
- ax = _ax;
- ay = _ay;
-
- timeleft = duration;
- ptimer = 0;
-
- add((cList **)list);
- }
-
- cMovable::cMovable(cProperties *_orig)
- : cAudible(_orig)
- {
- pushes = 0;
- stop_movement();
-
- resting = FALSE;
- horizontal_friction = orig->params->get_bool("*HORIZONTAL_FRICTION", TRUE);
-
- bounce_loss = orig->params->get_int("*BOUNCE_LOSS", 3);
- one_time_bounce = orig->params->get_bool("*ONE_TIME_BOUNCE", FALSE);
-
- last_bounce_y = MAXINT;
- }
-
- cMovable::~cMovable()
- {
- pushes->delete_list();
- }
-
- int cMovable::control()
- {
- fix dx, dy, dv, dt;
-
- // Do changing of picture
-
- cAudible::control();
-
- // Save old variables
-
- ox = x, oy = y;
-
- // Compute time difference
-
- dt = vtimer.delta();
-
- // Do movement in x direction
-
- dv = ax * dt;
- dx = (vx + (dv >> 1)) * dt;
- vx += dv;
-
- // Do movement in y direction
-
- dv = ay * dt;
- dy = (vy + (dv >> 1)) * dt;
- vy += dv;
-
- // Handle pushes
-
- cPush *n;
- int kill;
-
- for (cPush *p = pushes; p != 0; p = n)
- {
- n = (cPush *)p->next;
-
- // First get time difference
-
- dt = p->ptimer.delta();
-
- // Check if push is ending here
-
- if (dt > p->timeleft)
- {
- dt = p->timeleft;
- kill = TRUE;
- }
- else
- {
- p->timeleft -= dt;
- kill = FALSE;
- }
-
- // Do movement in x direction
-
- dv = p->ax * dt;
- dx += (p->vx + (dv >> 1)) * dt;
- p->vx += dv;
-
- // Do movement in y direction
-
- dv = p->ay * dt;
- dy += (p->vy + (dv >> 1)) * dt;
- p->vy += dv;
-
- // Check if there's some time left
-
- if (kill)
- {
- // Add aquired speed permanently
-
- vx += p->vx, vy += p->vy;
-
- // Remove this object
-
- delete p;
- }
- }
-
- // Set new position
-
- if (dx != (fix)0 || dy != (fix)0)
- set_position(fx + dx, fy + dy);
-
- return TRUE;
- }
-
- int cMovable::check_resting_on_boundaries(cLine *obj, cDisplayable *bound)
- {
- // The function computes the boundary lines (for obj) before and after
- // movement computed by control(). It forms a square with the new
- // boundary line as base and the height of the previous boundary line
- // as height.
- //
- // After that it searches for lines that fall in the area and picks out
- // the first the object would encounter. The object is placed correctly
- // interpolated on this line. This function only detects lines when the
- // object is going down.
-
- ASSERT(obj != 0);
-
- // Get old (oy1) and new (y1, x1, x2) boundary line for object
-
- int oy1 = oy - obj->y1, y1 = y - obj->y1;
-
- int x1 = x + obj->x1, x2 = x + obj->x2;
- sort2(x1, x2);
-
- // Consider only the case: oy1 >= y1, we're going down
-
- if (oy1 >= y1)
- {
- int y_max = MININT;
-
- // Loop through displayables
-
- for (cDisplayable *d = bound; d != 0; d = (cDisplayable *)d->next)
- {
- // Loop trough lines
-
- for (cLine *l = d->line_bounds; l != 0; l = (cLine *)l->next)
- {
- int lx1 = d->x + l->x1, lx2 = d->x + l->x2, ly1 = d->y - l->y1;
-
- sort2(lx1, lx2);
-
- if (lx2 >= x1 && x2 >= lx1 && oy1 >= ly1 && ly1 >= y1)
- y_max = ly1;
- }
- }
-
- // If there was a boundary, set the object on this boundary
-
- if (y_max > MININT)
- {
- // Put object on the edge
-
- set_position(fx, (fix)(y_max + obj->y1));
-
- // We're on boundary
-
- return TRUE;
- }
- }
-
- // We're not on boundary
-
- return FALSE;
- }
-
- void cMovable::bounce_on_boundaries()
- {
- // This function is an extension from check_resting_on_boundaries,
- // it handles the bouncing and resting of objects on platforms.
-
- // Bounce on walls
-
- if (!x_on_screen())
- vx = -vx / bounce_loss;
-
- // Check if we fell onto something
-
- if (orig->line != 0
- && (!one_time_bounce || oy < last_bounce_y)
- && check_resting_on_boundaries(line_bounds, structures)
- )
- {
- // Modify horizontal speed
-
- if (horizontal_friction)
- {
- if (abs(vx) < (fix)10)
- vx = 0;
- else if (!is_x_pushed())
- new_x_push(0.5, 0, -vx);
- }
-
- // Modify vertical speed, when velocity is smaller than 70 upwards
- // (approx 10 pixels total movement) set status to resting
-
- if ((vy >= (fix)0 && vy < (fix)70) || resting)
- vy = 0, resting = TRUE;
- else
- vy = -vy / bounce_loss;
-
- last_bounce_y = y;
- }
- else
- {
- resting = FALSE;
- }
-
- // Set vertical acceleration
-
- ay = GRAVITY;
- }
-
- void cMovable::move_objects_on_boundaries(cLine *obj, cDisplayable *bound)
- {
- // The function computes the boundary lines (for obj) before and after
- // movement computed by control(). It forms a square with the new
- // boundary line as base and the height of the previous boundary line
- // as height.
- //
- // After that it searches for lines that fall in the area and moves all
- // objects by the amount this movable moved.
-
- ASSERT(obj != 0);
-
- // Get old and new (y1, y2, x1, x2) boundary lines for object
-
- int y1 = oy - obj->y1, y2 = y - obj->y1;
- sort2(y1, y2);
-
- int x1 = x + obj->x1, x2 = x + obj->x2;
- sort2(x1, x2);
-
- // Loop through displayables
-
- for (cDisplayable *d = bound; d != 0; d = (cDisplayable *)d->next)
- {
- // Loop trough lines
-
- for (cLine *l = d->line_bounds; l != 0; l = (cLine *)l->next)
- {
- int lx1 = d->x + l->x1, lx2 = d->x + l->x2, ly1 = d->y - l->y1;
-
- sort2(lx1, lx2);
-
- if (lx2 >= x1 && x2 >= lx1 && y1 <= ly1 && ly1 <= y2)
- {
- d->make_dirty();
- d->set_position(d->fx + (fix)(x - ox), fy - (fix)obj->y1 + (fix)l->y1);
- d->make_dirty();
- }
- }
- }
- }
-
- cDisplayable *cMovable::check_radial_boundaries(cCircle *obj, cDisplayable *bound, int (*callback)(cMovable *, cDisplayable *, cCircle *, cCircle *), cDisplayable *ignore, int reposition)
- {
- // This function determines if there is any object in "bound"
- // that is in radius of circle(s) "obj" and calls callback (if
- // not zero) for every object. It returns the closest object.
-
- // Distance that object travelled in x and y direction
-
- int dx = x - ox, dy = y - oy;
-
- // Distance squared
-
- int dd = d_square(dx, dy);
-
- // Closest object, minimum distance found and position of that object
-
- cDisplayable *closest = 0, *last_callback = 0;
- int d_min = MAXINT, closest_x = 0, closest_y = 0;
-
- // Loop through circles
-
- for (cCircle *o = obj; o != 0; o = (cCircle *)o->next)
- {
- // Start position
-
- int sx = ox + o->x, sy = oy - o->y;
-
- // End position
-
- int ex = x + o->x, ey = y - o->y;
-
- // Loop through objects
-
- for (cDisplayable *d = bound; d != 0; d = (cDisplayable *)d->next)
- {
- // Loop through circles for every object
-
- if (d != this && d != ignore)
- for (cCircle *c = d->circle_bounds; c != 0; c = (cCircle *)c->next)
- {
- // Locus of circle of object d, circle c
-
- int cx = d->x + c->x, cy = d->y - c->y;
-
- // Point of closest approach fraction: t/dd of whole vector
-
- int t = (cx - sx) * dx + (cy - sy) * dy;
-
- // If t < 0 or t > length of vector choose end points, else
- // choose closest point
-
- int qx, qy;
-
- if (t <= 0)
- qx = sx, qy = sy;
- else if (t >= dd)
- qx = ex, qy = ey;
- else
- qx = sx + t*dx/dd, qy = sy + t*dy/dd;
-
- // Compute distance
-
- int dist = d_square(cx - qx, cy - qy);
-
- // Check if it is in range
-
- if (dist < square(o->radius + c->radius))
- {
- if (callback != 0 && (last_callback == d || !callback(this, d, o, c)))
- continue;
-
- last_callback = d;
-
- if (dist - c->radius < d_min)
- {
- d_min = dist - c->radius;
-
- closest = d;
- closest_x = qx;
- closest_y = qy;
- }
- }
- }
- }
- }
-
- // Move to closest position
-
- if (reposition && closest != 0)
- set_position(closest_x, closest_y);
-
- // Return closest object
-
- return closest;
- }
-
- void cMovable::add_position(fix dx, fix dy)
- {
- set_position(fx + dx, fy + dy);
- }
-
- void cMovable::add_angular_position(fix d, fix angle)
- {
- set_position(fx + d * cos(angle), fy + d * sin(angle));
- }
-
- fix cMovable::get_speed(fix angle)
- {
- return vx * cos(angle) + vy * sin(angle);
- }
-
- void cMovable::set_angular_speed(fix v, fix angle)
- {
- vx = v * cos(angle);
- vy = v * sin(angle);
- }
-
- void cMovable::add_angular_speed(fix v, fix angle)
- {
- vx += v * cos(angle);
- vy += v * sin(angle);
- }
-
- fix cMovable::get_acceleration(fix angle)
- {
- return ax * cos(angle) + ay * sin(angle);
- }
-
- void cMovable::set_angular_acceleration(fix a, fix angle)
- {
- ax = a * cos(angle);
- ay = a * sin(angle);
- }
-
- void cMovable::add_angular_acceleration(fix a, fix angle)
- {
- ax += a * cos(angle);
- ay += a * sin(angle);
- }
-
- int cMovable::is_x_pushed()
- {
- for (cPush *p = pushes; p != 0; p = (cPush *)p->next)
- if (p->vx != (fix)0 || p->ax != (fix)0)
- return TRUE;
-
- return FALSE;
- }
-
- int cMovable::is_y_pushed()
- {
- for (cPush *p = pushes; p != 0; p = (cPush *)p->next)
- if (p->vy != (fix)0 || p->ay != (fix)0)
- return TRUE;
-
- return FALSE;
- }